#!/usr/bin/env python
#
# makehtml.py
#
# Version : 0.7
# Date : 20080705
#
# Author : Bernard Czenkusz
# Email  : bernie@skipole.co.uk

#
# makehtml.py - create html pages, run as part of skipolemonitor
# Copyright (c) 2007 Bernard Czenkusz
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

"""Creates the html pages for Skipole Monitor"""

from net import *

import os
import sys
import cgi
import shutil
import time

# The function cgi.escape( s[, quote]) is used to make sure <, >, & are escaped
# when the text names and descriptions are sent to html

# Make HTML header and footer strings

header=""
# global header variable contents are set in the make_header() function

group_style="""
 <link href="style.css" rel="stylesheet" type="text/css" />
 <link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
"""

host_style="""
 <link href="../style.css" rel="stylesheet" type="text/css" />
 <link href="../images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
"""

footer="""</body>
</html>
"""


def new_httproot():
    """Clear out and remake the http root folder"""
    global SCRIPTDIRECTORY
    #
    # The HTTPROOT folder must exist
    if not os.path.exists(net_cfg["HTTPROOT"]): return 0
    #
    # Clear out its contents
    # del_dir_contents is a funtion in the net module
    if not del_dir_contents(net_cfg["HTTPROOT"]): return 0
    #
    # Copy style.css from SCRIPTDIRECTORY to net_cfg["HTTPROOT"]
    sourcefilepath=os.path.join(SCRIPTDIRECTORY,"style.css")
    destinationfilepath=os.path.join(net_cfg["HTTPROOT"],"style.css")
    shutil.copyfile(sourcefilepath, destinationfilepath)
    #
    # create net_cfg["HTTPROOT"]/images,
    dstfolder=os.path.join(net_cfg["HTTPROOT"],"images")
    os.mkdir(dstfolder)
    #
    # copy image file names
    imagefiles = os.listdir(net_cfg["IMAGES_FOLDER"])
    for filename in imagefiles:
        srcpath = os.path.join(net_cfg["IMAGES_FOLDER"], filename)
        dstpath = os.path.join(dstfolder, filename)
        shutil.copyfile(srcpath, dstpath)
    #
    # Copy icon file favicon.ico to images folder
    # This is for the url and favourites icon
    sourcefilepath=os.path.join(SCRIPTDIRECTORY,"favicon.ico")
    destinationfilepath=os.path.join(dstfolder,"favicon.ico")
    shutil.copyfile(sourcefilepath, destinationfilepath)
    return 1


def make_header():
    "Creates header by adding browser refresh time"
    global net_cfg
    global header
    #
    # Add BROWSER_REFRESH time to header
    header="""
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
 <meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
 <meta http-equiv="Content-Language" content="en-us" />
 <meta http-equiv="pragma" content="no-cache" />
"""
    header += " <meta http-equiv=\"refresh\" content=\"" + net_cfg["BROWSER_REFRESH"] + "\" />\n"
    #
    return


def network_loading():
    """Make a 'network loading' html page"""
    global header
    global group_style
    global footer
    global net_cfg
    # 
    # Create file contents
    contents = header + group_style
    # Create the page title
    contents += "<title>Skipole Monitor</title>\n"
    contents += "</head>\n<body>\n"
    contents += "<p><br /><br /><br /><br />Please wait - Network loading</p>"
    contents += footer
    # and write the page out
    make_file("index.html", net_cfg["HTTPROOT"], contents)
    return



def make_file(filename, folder, contents):
    """Opens and writes the contents to the file"""
    filepath=os.path.join(folder,filename)
    # Try to write the file, if fail, wait one second, try again,
    # then fail silently without causing any errors
    try:
        fp=open(filepath, 'w')
        fp.write(contents)
        fp.close()
    except:
        # Failed, maybe file is being read via the web server
        # so wait a second and try again
        time.sleep(1)
        try:
            fp=open(filepath, 'w')
            fp.write(contents)
            fp.close()
        except:
            # Its no use - cannot write the file, so exit
            pass
    return


def makehtml(group_id, subgroups_in_group, hosts_in_group):
    """Make a group html page"""
    global header
    global group_style
    global footer
    global net_cfg
    # Group zero - the alerts group is special, and not dealt with
    # by this function
    if group_id == 0: return
    # Create filename and folder
    # if group_id is 1, this is the top page
    if group_id == 1:
        filename="index.html"
    else:
        filename=str(group_id) + ".html"
    folder=net_cfg["HTTPROOT"]
    # Create file contents
    contents = header + group_style
    # Create the page title, from the short name
    if group_id == 1:
        contents += "<title>Skipole Monitor</title>\n"
    else:
        contents += "<title>" + cgi.escape(get_net(group_id, NAME)) + "</title>\n"
    # Create javascript within the header 
    contents += help_javascript()
    # Finish the header and start the body
    contents += "</head>\n<body>\n"
    # Create the page name, from item short name, status, and description
    contents += page_name(get_net(group_id, NAME), get_net(group_id, STATUS), get_net(group_id, DESCRIPTION))
    # Put in the links to the parent page
    contents += parent_links(group_id)
    # Thats the page title and description, now add the list
    # of subgroups and hosts on this page
    # If group_id is 1, this is the top page, and so must show
    # a link to the All Alerts page
    if group_id == 1:
        contents += "<p><br />"
        contents += subgroup_html("0.html", get_net(0, STATUS), get_net(0, NAME))+"</p>\n"
    contents += list_groups_and_hosts(subgroups_in_group, hosts_in_group)
    # Create the help icon
    contents += "<div id=\"helpicon\" onclick=\"toggleHelp('HelpBox')\">Help</div>\n"
    # Add the page help text
    if group_id == 1:
        contents += help_box("toppage")
    else:
        contents += help_box("group")
    # and the page footer
    contents += footer
    # and write the page out
    make_file(filename, folder, contents)
    return

    
def makealertspage(all_alerts_list):
    """Make the alerts page"""
    global header
    global group_style
    global footer
    global net_cfg
    # Create the filename and folder
    filename="0.html"
    folder=net_cfg["HTTPROOT"]
    # Create file contents
    contents = header + group_style
    # Create the page title, from the short name
    contents += "<title>" + cgi.escape(get_net(0, NAME)) + "</title>\n"
    # Create javascript within the header 
    contents += help_javascript()
    # Finish the header and start the body
    contents += "</head>\n<body>\n"
    # Create the page name, from device short name, status, and description
    contents += page_name(get_net(0, NAME), get_net(0, STATUS), get_net(0, DESCRIPTION))
    # Put in a link to the top page
    contents += "<div id=\"backlinks\">"
    top_status = get_net(1, STATUS)
    set_color = "<a class=\"is_white\""  
    if top_status == 1:
        set_color = "<a class=\"is_green\""
    if top_status == 2:
        set_color = "<a class=\"is_yellow\""
    if top_status == 3:   
        set_color = "<a class=\"is_red\""
    contents += set_color
    contents += " href=\"index.html\" onfocus=\"if(this.blur)this.blur();\">"
    contents += cgi.escape(get_net(1, NAME)) + "</a></div>\n"
    # Now add the list of alerting hosts
    if len(all_alerts_list)==0:
        # If there are no alerting hosts, say so
        contents += "<h1 class=\"is_green\">NO ALERTING HOSTS</h1>\n"
    else:
        # List each host, and its status
        contents += "<table>\n"
        rowcounter=0
        # sort by alphabetical name order
        all_alerts_list.sort(lambda a,b:cmp(get_net(a, NAME),get_net(b, NAME)))
        for item in all_alerts_list:
            if rowcounter==0:
                contents += "<tr>\n"
            linkto = str(item) + "/index.html"
            status = get_net(item, STATUS)
            hostname=get_net(item, NAME)
            contents += " <td>" + host_html(linkto, status, hostname) + "</td>\n"
            rowcounter = rowcounter+1
            if rowcounter == 4:
                contents += "</tr>\n"
                rowcounter = 0
        if rowcounter == 1 : contents += " <td></td>\n <td></td>\n <td></td>\n</tr>\n"
        if rowcounter == 2 : contents += " <td></td>\n <td></td>\n</tr>\n"
        if rowcounter == 3 : contents += " <td></td>\n</tr>\n" 
        contents += "</table>\n"            
    # Create the help icon
    contents += "<div id=\"helpicon\" onclick=\"toggleHelp('HelpBox')\">Help</div>\n"
    # Add the page help text
    contents += help_box("alertpage")
    # and the page footer
    contents += footer
    # and write the page out
    make_file(filename, folder, contents)
    return
                  

def page_name(name, status, description):
    """Returns the html of the page name and description"""
    contents = "<h1 class=\"is_white\">"
    if status == 1:
        contents = "<h1 class=\"is_green\">"
    if status == 2:
        contents = "<h1 class=\"is_yellow\">"    
    if status == 3:
        contents = "<h1 class=\"is_red\">"
    contents += cgi.escape(name) + "<span class=\"description\">" + cgi.escape(description) + "</span></h1>\n"
    return contents
    

def list_groups_and_hosts(subgroups_in_group, hosts_in_group):
    """Returns html listing each subgroup and host, with their color status for a group page"""
    contents = "\n<table>\n"
    rowcounter=0
    # sort by alphabetical name order
    subgroups_in_group.sort(lambda a,b:cmp(get_net(a, NAME),get_net(b, NAME)))
    for item in subgroups_in_group:
        if rowcounter==0:
            contents += "<tr>\n"
        linkto = str(item) + ".html"
        status = get_net(item, STATUS)
        groupname=get_net(item, NAME)
        contents += " <td>" + subgroup_html(linkto, status, groupname) + "</td>\n"
        rowcounter = rowcounter+1
        if rowcounter == 4:
            contents += "</tr>\n"
            rowcounter = 0
    if rowcounter == 1 : contents += " <td></td>\n <td></td>\n <td></td>\n</tr>\n"
    if rowcounter == 2 : contents += " <td></td>\n <td></td>\n</tr>\n"
    if rowcounter == 3 : contents += " <td></td>\n</tr>\n"       
    # List each host, and its status
    rowcounter=0
    # sort by alphabetical name order
    hosts_in_group.sort(lambda a,b:cmp(get_net(a, NAME),get_net(b, NAME)))
    for item in hosts_in_group:
        if rowcounter==0:
            contents += "<tr>\n"
        linkto = str(item) + "/index.html"
        status = get_net(item, STATUS)
        hostname=get_net(item, NAME)
        contents += " <td>" + host_html(linkto, status, hostname) + "</td>\n"
        rowcounter = rowcounter+1
        if rowcounter == 4:
            contents += "</tr>\n"
            rowcounter = 0
    if rowcounter == 1 : contents += " <td></td>\n <td></td>\n <td></td>\n</tr>\n"
    if rowcounter == 2 : contents += " <td></td>\n <td></td>\n</tr>\n"
    if rowcounter == 3 : contents += " <td></td>\n</tr>\n" 
    contents += "</table>\n"
    return contents
    
    
def subgroup_html(linkto, status, groupname):
    """Returns the html to display a subgroup link"""
    contents = "<a class=\"whitegroup\""
    if status == 1:
        contents = "<a class=\"greengroup\""
    if status == 2:
        contents = "<a class=\"yellowgroup\""
    if status == 3:   
        contents = "<a class=\"redgroup\""
    contents += " href=\"" + linkto + "\" onfocus=\"if(this.blur)this.blur();\">" + cgi.escape(groupname) + "</a>"
    return contents
    
    
def host_html(linkto, status, hostname):
    """Returns the html to display a host link"""
    contents = "<a class=\"whitehost\""
    if status == 1:
        contents = "<a class=\"greenhost\""
    if status == 2:
        contents = "<a class=\"yellowhost\""
    if status == 3:   
        contents = "<a class=\"redhost\""
    contents += " href=\"" + linkto + "\" onfocus=\"if(this.blur)this.blur();\">" + cgi.escape(hostname) + "</a>"
    return contents

       
def host_page(item):
    """Create a host html page"""
    global header
    global host_style
    global footer
    global net_cfg
    #
    # Create a folder for the html files for this item
    folder=os.path.join(net_cfg["HTTPROOT"],str(item))
    # if the folder does not exist, create it
    if not os.path.exists(folder):
        os.mkdir(folder)
    #
    # Check a log.html exists, if it doesn't, put in a blank
    if not os.path.exists(os.path.join(folder, "log.html")):
        make_file("log.html", folder, "<body>\n<pre>\nWaiting for poll . . .\n</pre>\n</body>")
    #
    # Create the html page for this host
    filename="index.html"
    # Create file contents
    contents = header + host_style
    # Create the page title, from the short name
    contents += "<title>" + cgi.escape(get_net(item, NAME)) + "</title>\n"
    # Create javascript within the header 
    contents += help_javascript()
    # Finish the header and start the body
    contents += "</head>\n<body>\n"
    # Create the page name, from device short name, status, and description
    contents += page_name(get_net(item, NAME), get_net(item, STATUS), get_net(item, DESCRIPTION))
    # Put in the links to the parent page
    contents += parent_links(item)
    # Put in the item ip address
    contents += "<p>Host ip address : " + get_net(item, ADDRESS) + "</p>\n"
    # Add a message if status unknown
    if get_net(item, STATUS) == 0:
        contents += "<p>Status unknown - waiting for next poll</p>\n"
    # Add the graph title
    contents += "<br />\n<p style=\"font-size: 12px\">Round trip time (msec)</p>\n"
    # Add the rrd graph image
    contents += "<div class=\"graphimage\"><img src=\"log.png\" alt=\"Ping round trip time graph\" /></div>"
    # Add an iframe - this shows the log.html page
    contents += "<br />\n<iframe id=\"logs\" src=\"log.html\"></iframe>\n"
    # Create the help icon
    contents += "<div id=\"helpicon\" onclick=\"toggleHelp('HelpBox')\">Help</div>\n"
    # Add the page help text
    contents += help_box("host")
    contents += footer 
    # And write the file contents
    make_file(filename, folder, contents)
    return

    
def parent_links(item):
    "Return html showing the navigation links of parent groups"""
    # top page doesn't have a parent links string
    if item == 1 : return ""
    next_item = item
    contents = ""
    while next_item != 1:
        next_item, element = link_element(next_item, item)
        contents=element + contents
    return "<div id=\"backlinks\">" + contents + "</div>\n"

    
def link_element(next_item, first_item):
    "Create each element of the parent navigation links""" 
    parent_id=get_net(next_item, PARENT)
    parent_name=get_net(parent_id, NAME)
    if parent_id == 1:
        parent_html = "index.html"
    else:
        parent_html=str(parent_id) + ".html"
    status = get_net(parent_id, STATUS)
    contents = "<a class=\"is_white\""  
    if status == 1:
        contents = "<a class=\"is_green\""
    if status == 2:
        contents = "<a class=\"is_yellow\""
    if status == 3:   
        contents = "<a class=\"is_red\""
    if is_net_group(first_item):
        contents += " href=\""
    else:
        # As this is a host, the parent link is up a directory    
        contents += " href=\"../"
    contents += parent_html + "\" onfocus=\"if(this.blur)this.blur();\">" + cgi.escape(parent_name) + "</a>"
    if parent_id != 1:
        contents = " : " + contents
    return parent_id, contents

    
def help_box(item_type):
    """Creates html help boxes, depending on which page the help icon is pressed"""
    # Displays the help box, the help contents differ if
    # item_type is "toppage", "group", "host" or "alertpage"
    contents = "<div id=\"HelpBox\" class=\"hiddenhelp\">\n"
    # Set polling interval, as it appears in the help text
    poll_interval = "five minutes"
    if net_cfg["POLL_INTERVAL"]=="60":
        poll_interval = "minute"
    if net_cfg["POLL_INTERVAL"]=="120":
        poll_interval = "two minutes"
    if net_cfg["POLL_INTERVAL"]=="600":
        poll_interval = "ten minutes"
    # set default help text
    helptext = """
<div id="closehelp"><img src="images/close.png" alt="Close" onclick="hideHelp('HelpBox')" /></div>
<div class="helpstyle">No help found.</div>
"""
    if item_type == "toppage":
        helptext = """
<div id="closehelp"><img src="images/close.png" alt="Close" onclick="hideHelp('HelpBox')" /></div>
<div class="helpstyle">
This page can show groups or hosts.<br />
<br />
Sub-groups can be added, which in turn contain further groups and hosts.<br />
<br />
Groups and hosts are edited via the Skipole Monitor GUI interface
running on the web server.<br />
<br />
Each host is pinged four times every """
        helptext += poll_interval
        helptext += """.<br />
If three or four pings reply, the host is shown as green.<br />
One or two replies; the host turns yellow.<br />
No replies; the host turns red.<br />
<br />
The groups reflect the worst case of any host beneath them.<br />
<br />
The 'All Alerts' group is a special group which shows
all alerting yellow or red hosts.<br />
<br />
On startup, the web site may show hosts and groups as
white - their status has not yet been determined.<br />
<br />
For further information:
<a href="http://www.skipole.net" target="_blank">www.skipole.net</a><br />
</div>
"""
    if item_type == "group":
        helptext = """
<div id="closehelp"><img src="images/close.png" alt="Close" onclick="hideHelp('HelpBox')" /></div>
<div class="helpstyle">
This page can show groups or hosts.<br />
<br />
Each host is pinged four times every """
        helptext += poll_interval
        helptext += """.<br />
If three or four pings reply, the host is shown as green.<br />
One or two replies; the host turns yellow.<br />
No replies; the host turns red.<br />
<br />
The groups reflect the worst case of any host beneath them.<br />
<br />
The top line of groups can be used to navigate to parent
groups, and also shows their status (green, yellow, red).<br />
<br />
On startup, the web site may show hosts and groups as
white - their status has not yet been determined.<br />
<br />
For further information:
<a href="http://www.skipole.net" target="_blank">www.skipole.net</a><br />
</div>
"""
    if item_type == "host":
        helptext = """
<div id="closehelp"><img src="../images/close.png" alt="Close" onclick="hideHelp('HelpBox')" /></div>
<div class="helpstyle">
This page shows the status of this host.<br />
<br />
The top line of groups can be used to navigate to parent
groups, and also shows their status (green, yellow, red).<br />
<br />
Each host is pinged four times every """
        helptext += poll_interval
        helptext += """.<br />
<br />
If three or four pings reply, the host is shown as green.<br />
One or two replies; the host turns yellow.<br />
No replies; the host turns red.<br />
<br />
The graph shows the round trip times of the pings, with
solid red or yellow lines where pings have failed.<br />
<br />
For further information:
<a href="http://www.skipole.net" target="_blank">www.skipole.net</a><br />
</div>
"""
    if item_type == "alertpage":
        helptext = """
<div id="closehelp"><img src="images/close.png" alt="Close" onclick="hideHelp('HelpBox')" /></div>
<div class="helpstyle">
This page shows all alerting hosts.<br />
<br />
The administrator cannot add or delete content here as it
is automatically generated.<br />
<br />
Each host is pinged four times every """
        helptext += poll_interval
        helptext += """.<br />
<br />
No replies; the host turns red.<br />
One or two replies; the host turns yellow.<br />
<br />
If three or four pings reply, the host is shown as green,
and is then automatically removed from this page.<br />
<br />
Choose the top left link to return to the top page.<br />
<br />
For further information:
<a href="http://www.skipole.net" target="_blank">www.skipole.net</a><br />
</div>
"""
    contents += helptext + "</div>\n"
    return contents
    
    
def help_javascript():
    """Create the javascript, used in the html header, for toggling the help box"""
    contents = """
<script type="text/JavaScript" language="javascript">
    help_visible = 0
    function toggleHelp(objectID){
        var object = document.getElementById(objectID);
        if (help_visible == 0) {
            object.style.visibility = 'visible';
            help_visible = 1;
            }
         else {
            object.style.visibility = 'hidden';
            help_visible = 0;
            }
        }
    function hideHelp(objectID){
        var object = document.getElementById(objectID);
        object.style.visibility = 'hidden';
        help_visible = 0;
        }
        
</script>
"""
    return contents


def make_log_html(item):
    "Read lines from file log.txt, and create log.html"
    #
    # Check a folder for the html files for this item exists
    host_html_folder=os.path.join(net_cfg["HTTPROOT"],str(item))
    # if the folder does not exist, create it
    if not os.path.exists(host_html_folder):
        os.mkdir(host_html_folder)
    #
    host_data_folder=os.path.join(net_cfg["NETWORK_DATA_FOLDER"],str(item))
    #
    # filepath is the name of the log file, held in the host data folder
    filepath=os.path.join(host_data_folder,"log.txt")
    #
    if os.path.exists(filepath):
        # read LOG_RECORDS lines from file
        fp=open(filepath, 'r')
        content = "<body>\n<pre>\n"
        for line in fp:
            content += line
        fp.close()
        content += "</pre>\n</body>"
        # And write the file contents
        make_file("log.html", host_html_folder, content)
    else:
        # log.txt doesn't exist so create a temporary log.html containing no data
        make_file("log.html", host_html_folder, "<body>\n<pre>\nWaiting for poll . . .\n</pre>\n</body>")
    return


def del_html_page(item):
    """Takes a group html file or host directory, and creates
       a page pointing back to the site index, so users with this
       in their history will be re-directed to the top page."""
    global header
    global group_style
    global footer
    #
    # Do not alter item 0 or item 1 - alerts page and top page
    if item < 2: return
    # If the item is a group, re-write its html page
    if is_net_group(item):
        filename=str(item) + ".html"
        filepath=os.path.join(net_cfg["HTTPROOT"],filename)
        if not os.path.exists(filepath):return
        # 
        # Create file contents
        contents = header + group_style
        # Create the page title
        contents += """<title>Skipole Monitor</title>
</head>
<body>
<script language="Javascript" type="text/javascript">
window.top.location="index.html";
</script>
<p><br /><br /><br /><br />This group has been deleted</p>
"""
        contents += footer
        # and write the page out
        make_file(filename, net_cfg["HTTPROOT"], contents)
        return
    #
    # So this item is a host
    # Get the folder for the html files for this item
    folder=os.path.join(net_cfg["HTTPROOT"],str(item))
    #
    # if the folder does not exist, return
    if not os.path.exists(folder):return
    #
    # Check the folder is a directory
    if not os.path.isdir(folder):return
    #
    # Delete the directory contents using
    # del_dir_contents from the net module
    del_dir_contents(folder)
    #
    # And create the new index to put in this folder
    filename="index.html"
    # Create file contents
    contents = header + host_style
    # Create the page
    contents += """<title>Skipole Monitor</title>
</head>
<body>
<script language="Javascript" type="text/javascript">
window.top.location="../index.html";
</script>
<p><br /><br /><br /><br />This item has been deleted</p>
"""
    contents += footer
    # and write the page out
    make_file(filename, folder, contents)
    return


